package com.bs.demo.config;

import com.alibaba.fastjson.JSONObject;
import com.bs.demo.vo.TestVo;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * @author bingshao
 * @date 2022/12/7
 **/
@ControllerAdvice
@Log4j2
public class LogRequestBodyAdvice implements RequestBodyAdvice {

    private static final String ENCODING = "UTF-8";


    // 是否开启拦截 true开启 false不开启
    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        // 选择类型很多，比如自定义注解
//        methodParameter.hasMethodAnnotation();
//        methodParameter.hasParameterAnnotation();
        return true;
    }

    /**
     * 请求体解析前处理
     *
     * @param httpInputMessage
     * @param methodParameter
     * @param type
     * @param aClass
     * @return
     * @throws IOException
     */
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        // httpInputMessage.getHeaders();  获取请求头

        // 常见的业务仓场景有: 1 记录日志 2 内容加密解密...
        log.info("拦截到的请求参数为 = {}", methodParameter.toString());
        Method method = methodParameter.getMethod();
        log.info("请求体读取前={}==>{}==>{}==>{}", method.getDeclaringClass().getSimpleName(), method.getName(), type.toString(), aClass.getName());
        return new SecurityInputMessage(httpInputMessage);
    }

    /**
     * 请求体解析后处理
     *
     * @param o
     * @param httpInputMessage
     * @param methodParameter
     * @param type
     * @param aClass
     * @return
     */
    @Override
    public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        Method method = methodParameter.getMethod();
        log.info("请求体读取后={}.{}:{}", method.getDeclaringClass().getSimpleName(), method.getName(), o.toString());
        return o;
    }

    /**
     * 处理没有参数
     *
     * @param o
     * @param httpInputMessage
     * @param methodParameter
     * @param type
     * @param aClass
     * @return
     */
    @Override
    public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        Method method = methodParameter.getMethod();
        log.info("没有参数={}.{}", method.getDeclaringClass().getSimpleName(), method.getName());
        return o;
    }

    @Data
    class SecurityInputMessage implements HttpInputMessage {

        private HttpHeaders headers;
        private InputStream body;

        @Override
        public InputStream getBody() {
            return this.body;
        }

        @Override
        public HttpHeaders getHeaders() {
            return this.headers;
        }

        public SecurityInputMessage(HttpInputMessage httpInputMessage) throws IOException {
            String body = IOUtils.toString(httpInputMessage.getBody(), ENCODING);
            log.info("拦截到的入参为 = {}", body);
            // 此处可以修改入参，比如解密参数
            if (StringUtils.isNotBlank(body)) {
                TestVo testVo = JSONObject.parseObject(body, TestVo.class);
                testVo.setName("云澈");
                body = JSONObject.toJSONString(testVo);
            }
            this.body = IOUtils.toInputStream(body, ENCODING);
            this.headers = httpInputMessage.getHeaders();
        }
    }
}
